﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace FlyingCell
{
    /// <summary>
    /// Defines bounding boxes that can be used on the 3D scene to detect entry, exit, collision, intrusion... of 3D objects
    /// </summary>
    public class BoundingBox
    {
        /// <summary>
        /// Enumerate the possible types of bounding box
        /// </summary>
        public enum BoundingBoxType
        {
            NONE,          // Infinite box
            CUBIC_ON_AXIS, // A cube aligned with the 3D axis
            SPHERIC,       // A sphere
            MESH,          // A complex bounding box given by a mesh
        }

        /// <summary>
        /// This bounding box type
        /// </summary>
        public BoundingBoxType mBoxType
        {
            get
            {
                return _mBoxType;
            }
        }
        private BoundingBoxType _mBoxType = BoundingBoxType.NONE;

        // CUBIC_ON_AXIS
        private Vector CubicOnAxis_XRange;
        private Vector CubicOnAxis_YRange;
        private Vector CubicOnAxis_ZRange;

        // SPHERIC
        private Point3D Spheric_Center;
        private double Spheric_Radius;

        // MESH
        private MeshGeometry3D Mesh_Geometry;

        /// <summary>
        /// Default constructor
        /// </summary>
        public BoundingBox()
        {
            _mBoxType = BoundingBoxType.NONE;
        }
        
        /// <summary>
        /// Creates a simple cubic bounding box parallele to the 3D axis (CUBIC_ON_AXIS)
        /// </summary>
        /// <param name="XRange">X range in which it is considered inside bounding box</param>
        /// <param name="YRange">Y range</param>
        /// <param name="ZRange">Z range</param>
        public BoundingBox(Vector XRange, Vector YRange, Vector ZRange)
        {
            _mBoxType = BoundingBoxType.CUBIC_ON_AXIS;

            CubicOnAxis_XRange = XRange;
            CubicOnAxis_YRange = YRange;
            CubicOnAxis_ZRange = ZRange;
        }

        /// <summary>
        /// Creates a spheric bounding box
        /// </summary>
        /// <param name="Center">The sphere center</param>
        /// <param name="Radius">The radius</param>
        public BoundingBox(Point3D Center, double Radius)
        {
            _mBoxType = BoundingBoxType.SPHERIC;

            Spheric_Center = Center;
            Spheric_Radius = Radius;
        }

        /// <summary>
        /// Creates a complex geometry bounding box based on meshes
        /// </summary>
        /// <param name="Geometry">The mesh</param>
        public BoundingBox(MeshGeometry3D Geometry)
        {
            _mBoxType = BoundingBoxType.MESH;

            Mesh_Geometry = Geometry;
        }

        /// <summary>
        /// Check if the point is in the cubic bounding box
        /// </summary>
        /// <param name="Point">Point to check</param>
        /// <returns>True if in, false else</returns>
        private bool IsPointInCubeOnAxis(Point3D Point)
        {
            bool RetVal = false;

            if ((Point.X >= CubicOnAxis_XRange.X) && (Point.X <= CubicOnAxis_XRange.Y) && 
                (Point.Y >= CubicOnAxis_YRange.X) && (Point.Y <= CubicOnAxis_YRange.Y) &&
                (Point.Z >= CubicOnAxis_ZRange.X) && (Point.Z <= CubicOnAxis_ZRange.Y))
            {
                RetVal = true;
            }
            return RetVal;
        }

        /// <summary>
        /// Check if the point is in the spheric bounding box
        /// </summary>
        /// <param name="Point">Point to check</param>
        /// <returns>True if in, false else</returns>
        private bool IsPointInSphere(Point3D Point)
        {
            bool RetVal = false;

            Vector3D TmpVector = new Vector3D(Point.X - Spheric_Center.X, Point.Y - Spheric_Center.Y, Point.Z - Spheric_Center.Z);

            if (TmpVector.Length <= Spheric_Radius)
            {
                RetVal = true;
            }

            return RetVal;
        }
        
        /// <summary>
        /// Check if the point is in the bounding box
        /// </summary>
        /// <param name="Point">Point to check</param>
        /// <returns>True if in, false else</returns>
        public bool IsPointInBoundingBox(Point3D Point)
        {
            bool RetVal = false;

            switch(_mBoxType)
            {
                case BoundingBoxType.NONE:
                    RetVal = true;
                    break;

                case BoundingBoxType.CUBIC_ON_AXIS:
                    if (IsPointInCubeOnAxis(Point))
                    {
                        RetVal = true;
                    }
                    break;

                case BoundingBoxType.SPHERIC:
                    if (IsPointInSphere(Point))
                    {
                        RetVal = true;
                    }
                    break;

                default:
                    break;
            }

            return RetVal;
        }

        /// <summary>
        /// Check if the geometry is in the bounding box, or partially in the box, or not.
        /// BE CAREFULL: Rotation calculation not yet working
        /// </summary>
        /// <param name="Geometry">The absolute Mesh</param>
        /// <param name="GeometryPosition">The 3D position of the geometry (offset to apply on each vertex)</param>
        /// <param name="GeometryRotation">The rotation to apply on the geometry (Not yet used)</param>
        /// <returns>Percentage of the geometry in the bounding box</returns>
        public int IsGeometryInBoundingBox(MeshGeometry3D Geometry, Point3D GeometryPosition, Point3D GeometryRotation)
        {
            if (_mBoxType == BoundingBoxType.NONE)
            {
                return 100;
            }
            
            int Percent = 0;
            int TotalPoint = 0;

            foreach (Point3D Point in Geometry.Positions)
            {
                bool TmpBool = false;

                Point3D TmpPoint = new Point3D(Point.X + GeometryPosition.X, Point.Y + GeometryPosition.Y, Point.Z + GeometryPosition.Z);

                // TODO: Add here rotation calculation

                switch (_mBoxType)
                {
                    case BoundingBoxType.NONE:
                        TmpBool = true;
                        break;

                    case BoundingBoxType.CUBIC_ON_AXIS:
                        TmpBool = IsPointInCubeOnAxis(TmpPoint);
                        break;

                    case BoundingBoxType.SPHERIC:
                        TmpBool = IsPointInSphere(TmpPoint);
                        break;

                    default:
                        break;
                }

                if (TmpBool)
                {
                    Percent++;
                }

                TotalPoint++;
            }

            Percent = Percent * 100;
            Percent = Percent / TotalPoint;

            return Percent;
        }
    }
}
